import { FormGroup, FormControl, FormArray } from '@angular/forms'
import { HttpParams } from '@angular/common/http'
import { JSEncrypt } from 'jsencrypt'
import { IHttpOptions } from '@shared/interfaces/decorator'
import {
  Country,
  ICurrencyOption,
  IExchangeRate,
  Currency,
  CardChanCodeValue,
  IChannelOption,
} from '@shared/interfaces/common'
import { Address } from '@shared/interfaces/address'
import { ISearchOption } from '@shared/interfaces/search'
import { ETradeChannel, ETransScene, EUserType, EPaymentBrand } from '@shared/enums'
import { User } from '@shared/interfaces/user'
import { customTradeSelRateLength, SUPPORT_CHANNELS } from './contraints'
import { IAamsItem } from '@shared/components/aams-select/aams-select.interface'
import { QrChannel, ECUPIChannel } from '@shared/interfaces/acquirer'
import { ITrade } from '@shared/interfaces/trade'
import { Channel } from '@shared/interfaces/channel'

export const generateOptions = (params): IHttpOptions => {
  let hp = new HttpParams()
  Object.keys(params).forEach(key => (hp = hp.append(key, params[key])))
  return {
    params: hp,
    withCredentials: true,
  }
}

export const transCountriesToOptions = (countries: Country[]) => {
  return countries.map(country => {
    return { label: country.name, value: country.codeAlpha3 }
  })
}

export const transCurrenciesToCurrencyOptions = (currencies: Currency[]): ICurrencyOption[] => {
  return currencies.map(currency => {
    return { label: currency.code + ' ' + currency.name, value: currency }
  })
}

export const transCurrenciesToValueOptions = (currencies: Currency[]): ISearchOption[] => {
  return currencies.map(currency => {
    return { label: currency.code + ' ' + currency.name, value: currency.code }
  })
}

export const generatePaymentBrandOptions = (): ISearchOption[] => {
  const options = []
  for (const val in ETradeChannel) {
    options.push({ label: ETradeChannel[val], value: val })
  }
  return options
}

export const generateTransSceneOptions = (): ISearchOption[] => {
  const options = []
  for (const val in ETransScene) {
    options.push({ label: ETransScene[val], value: val })
  }
  return options
}

export const generateOptionsFromEnum = (e: any): ISearchOption[] => {
  const options = []
  for (const val in e) {
    options.push({ label: e[val], value: val })
  }
  return options
}

export const transExchangeRateToOptions = (exchangeRates: IExchangeRate[]) => {
  return exchangeRates.map(rate => {
    return { label: rate.rateSponsor, value: rate }
  })
}

export const makeAllControlDirty = (form: FormGroup): void => {
  for (const key of Object.keys(form.controls)) {
    const current = form.controls[key]
    if (current instanceof FormControl) {
      current.markAsDirty()
      current.updateValueAndValidity()
    } else if (current instanceof FormArray) {
      for (const group of current.controls) {
        makeAllControlDirty(group as FormGroup)
      }
    } else if (current instanceof FormGroup) {
      makeAllControlDirty(current)
    }
  }
}

export const encryptPassword = (plainText: string): string => {
  const key =
    '-----BEGIN PUBLIC KEY-----\n' +
    'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDF4xejo7F1JVPU555mG6Kei8XU\n' +
    '2bT+V0Y+DaxzoBChaxYGOtlkf6vCh3y6Op/3OWdZAG8W17S3w9V7Skw0PvFvqqqc\n' +
    '8JLlnr9/zDKoit5X17VHX8Ky3jdl7Ll2h3MFghAbzcf0P7CRGxgpTm+lqsPQXETz\n' +
    'DEBEqXeE7Q7WeseaHQIDAQAB\n' +
    '-----END PUBLIC KEY-----'
  const rsa = new JSEncrypt()
  rsa.setPublicKey(key)
  return rsa.encrypt(plainText) || plainText // 加密有可能因为二次加密而失败，则把原字符串返回
}

export const encryptLongText = (longText: string): string[] => {
  return longText.split('\n').map(encryptPassword)
}

// custom validator to check that two fields match
export const MustMatch = (source: string, match: string) => {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[source]
    const matchingControl = formGroup.controls[match]
    if (matchingControl.errors && matchingControl.value && control.value !== matchingControl.value) {
      return
    }
    // set error on matchingControl if validation fails
    if (!matchingControl.value || control.value !== matchingControl.value) {
      matchingControl.setErrors({ mustMatch: true })
    } else {
      matchingControl.setErrors(null)
    }
  }
}

export const readableAddress = (fullAddress: Address): string => {
  const { nation, prefecture, city, address, address1, address2, zipCode } = fullAddress
  return `${prefecture} ${city} ${address} ${address1} ${address2} ${zipCode}`
}

export const compareTwoCurrencies = (c1: Currency, c2: Currency): boolean => {
  if (!c1 || !c2) return false
  return c1!.code === c2!.code
}

export const compareTwoCountries = (c1: Country, c2: Country): boolean => {
  if (!c1 || !c2) return false
  return c1 === c2
}

export const parseCurValue = (currencies: Array<any> = []): string => {
  return currencies.map(c => c.code).join(',')
}

// reference: https://stackoverflow.com/a/30810322
export const copyTextToClipboard = (text: string): boolean => {
  const textArea = document.createElement('textarea')

  // Place in top-left corner of screen regardless of scroll position.
  textArea.style.position = 'fixed'
  textArea.style.top = '0'
  textArea.style.left = '0'

  // Ensure it has a small width and height. Setting to 1px / 1em
  // doesn't work as this gives a negative w/h on some browsers.
  textArea.style.width = '2em'
  textArea.style.height = '2em'

  // We don't need padding, reducing the size if it does flash render.
  textArea.style.padding = '0'

  // Clean up any borders.
  textArea.style.border = 'none'
  textArea.style.outline = 'none'
  textArea.style.boxShadow = 'none'

  // Avoid flash of white box if rendered for any reason.
  textArea.style.background = 'transparent'

  textArea.value = text

  document.body.appendChild(textArea)
  textArea.focus()
  textArea.select()

  let isSuccessful = false

  try {
    isSuccessful = document.execCommand('copy')
  } catch (err) {}
  return isSuccessful
}

export const AmountStr2int = (amount: string, decimal: number): number => {
  let result
  if (decimal === 0) {
    result = parseInt(amount, 10)
  } else if (decimal > 0) {
    let decimalArray = []
    const amountArr = amount.split('.')
    const integerArray = amountArr[0].split('').map(Number)
    if (amountArr.length > 1) {
      decimalArray = amountArr[1].split('').map(Number)
    }
    if (decimalArray.length >= decimal) {
      decimalArray = decimalArray.slice(0, decimal)
    } else {
      const diff = decimal - decimalArray.length
      for (let i = 0; i < diff; i++) {
        decimalArray.push(0)
      }
    }
    const array = integerArray.concat(decimalArray).reverse()
    result = array.reduce((accumulator, currentValue, idx) => currentValue * Math.pow(10, idx) + accumulator, 0)
  }
  return result
}

export const openDownloadDialog = (url: any, saveName: string) => {
  if (typeof url === 'object' && url instanceof Blob) {
    url = URL.createObjectURL(url) // 创建blob地址
  }
  const aLink = document.createElement('a')
  aLink.href = url
  aLink.download = saveName
  aLink.click()
  aLink.remove()
}

export const getAAMSAcquirer = (user: User): IAamsItem => {
  const userType = user.userType
  switch (userType) {
    case EUserType.Agent:
    case EUserType.Institution:
    case EUserType.Merchant:
    case EUserType.Store: {
      return {
        num: user.insCode,
        code: user.insCode,
        name: user.insName,
        nameEN: user.insNameEN,
      }
    }
    default: {
      return {
        num: '',
        code: '',
        name: '',
        nameEN: '',
      }
    }
  }
}

export const getAAMSGroup = (user: User): IAamsItem => {
  const userType = user.userType
  switch (userType) {
    case EUserType.Agent:
    case EUserType.Merchant:
    case EUserType.Store: {
      return {
        num: user.agentCode,
        code: user.agentCode,
        name: user.agentName,
        nameEN: user.agentNameEN,
      }
    }
    default: {
      return {
        num: '',
        code: '',
        name: '',
        nameEN: '',
      }
    }
  }
}

export const getAAMSMerchant = (user: User): IAamsItem => {
  const userType = user.userType
  switch (userType) {
    case EUserType.Merchant:
    case EUserType.Store: {
      return {
        code: user.intMerCode,
        name: user.merName,
        nameEN: user.merNameEN,
        num: user.merNum,
      }
    }
    default: {
      return {
        num: '',
        code: '',
        name: '',
        nameEN: '',
      }
    }
  }
}

export const getAAMSStore = (user: User): IAamsItem => {
  const userType = user.userType
  switch (userType) {
    case EUserType.Store: {
      return {
        num: user.storeNum,
        code: user.intStoreCode,
        name: user.storeName,
        nameEN: user.storeNameEN,
      }
    }
    default: {
      return {
        num: '',
        code: '',
        name: '',
        nameEN: '',
      }
    }
  }
}

export const acquirerEditPermission = (authUser: User): boolean => {
  return (
    authUser.userType === EUserType.Institution ||
    authUser.userType === EUserType.Agent ||
    authUser.userType === EUserType.Merchant ||
    authUser.userType === EUserType.Store
  )
}

export const agentEditPermission = (authUser: User): boolean => {
  return (
    authUser.userType === EUserType.Agent ||
    authUser.userType === EUserType.Merchant ||
    authUser.userType === EUserType.Store
  )
}

export const merchantEditPermission = (authUser: User): boolean => {
  return authUser.userType === EUserType.Merchant || authUser.userType === EUserType.Store
}

export const storeEditPermission = (authUser: User): boolean => {
  return authUser.userType === EUserType.Store
}

export const setupUserRoleTypeSelectionPermit = (authUser: User, items: any): any => {
  const userTypePermissions = {}
  // setup the permission for each role, the high the level, the lower the score
  let permission = 0
  for (const key of Object.keys(EUserType)) {
    userTypePermissions[EUserType[key]] = permission
    permission++
  }
  const authUserTypePermission = userTypePermissions[authUser.userType]
  const list = items
  let userTypePermission: Number
  for (const l of list) {
    userTypePermission = userTypePermissions[l.key]
    if (authUserTypePermission < userTypePermission) {
      continue
    }
    items = items.filter(item => item.value !== l.value)
  }

  return items
}

export const parseSelRate = (selRate: number): string => {
  const intLength = getIntLengthOfFloat(selRate)
  const fixLength = customTradeSelRateLength - intLength
  return selRate.toString().includes('e') ? selRate.toFixed(fixLength) : selRate.toString()
}

export const getIntLengthOfFloat = (num: number): number => {
  /** 这里限定了 10 位，如果超出会截断，如果有字段超过 10 位小数，需要慎重使用 */
  return num.toFixed(10).split('.')[0].length
}

/**
 *
 * 只支持10位小数
 * @param num 待处理数值
 * @param m 10 的倍数，如 0.01 => 1，则 m 为2
 */
export const numScale = (num: number, m: number): number => {
  const flag = num < 0 ? -1 : 1
  num = Math.abs(num)
  // 拆分整数、小数部分
  let parts = num.toFixed(10).split('.')
  // 原始值的整数位数
  const integerLen = parts[0].length
  // 原始值的小数位数
  const decimalLen = parts[1] ? parts[1].length : 0

  // 放大，当放大的倍数比原来的小数位大时，需要在数字后面补零
  if (m > 0) {
    // 补多少个零：m - 原始值的小数位数
    let zeros = m - decimalLen
    while (zeros > 0) {
      zeros -= 1
      parts.push('0')
    }
    // 缩小，当缩小的倍数比原来的整数位大时，需要在数字前面补零
  } else {
    // 补多少个零：m - 原始值的整数位数
    let zeros = Math.abs(m) - integerLen
    while (zeros > 0) {
      zeros -= 1
      parts.unshift('0')
    }
  }

  // 小数点位置，也是整数的位数:
  //    放大：原始值的整数位数 + 放大的倍数
  //    缩小：原始值的整数位数 - 缩小的倍数
  const index = integerLen + m
  // 将每一位都拆到数组里，方便插入小数点
  parts = parts.join('').split('')
  // 当为缩小时，因为可能会补零，所以使用原始值的整数位数
  // 计算出的小数点位置可能为负，这个负数应该正好是补零的
  // 个数，所以小数点位置应该为 0
  parts.splice(index > 0 ? index : 0, 0, '.')

  return parseFloat(parts.join('')) * flag
}

export const numSub = (arg1: number, arg2: number): number => {
  let r1: number
  let r2: number
  let m: number
  let n: number
  try {
    r1 = arg1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = arg2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  m = Math.pow(10, Math.max(r1, r2)) // last modify by deeka //动态控制精度长度
  n = r1 >= r2 ? r1 : r2
  return +((arg1 * m - arg2 * m) / m).toFixed(n)
}

export const getObjectValue = (index: string, data: any): string | [] => {
  const keys = index.split('.')
  let value = data
  keys.forEach(key => {
    value = value[key]
  })
  return value
}

export const checkObjectEqual = (arr1: [] | {}, arr2: [] | {}): boolean => {
  return JSON.stringify(arr1) === JSON.stringify(arr2)
}

export const getChannelLogoURL = (brand: EPaymentBrand): string => {
  const obj = {
    ALP: 'assets/images/centaur/logo/Alipay-logo.png',
    WXP: 'assets/images/centaur/logo/weixin-logo.png',
    UPI: 'assets/images/centaur/logo/upi-logo.png',
    VIS: 'assets/images/centaur/logo/Visa-logo.svg',
    MCC: 'assets/images/centaur/logo/Mcc-logo.svg',
    AME: 'assets/images/centaur/logo/Amex-logo.svg',
    TRU: 'assets/images/centaur/logo/TRU-logo.jpg'
  }
  return obj[brand] || ''
}

export const omitEmptyValue = (params: { [key: string]: any }): { [key: string]: any } => {
  const newParams = {}
  Object.keys(params)
    .filter(key => {
      const value = params[key]
      if (typeof value === 'string') {
        return value !== ''
      }
      if (typeof value === 'number') {
        return true
      }
      return false
    })
    .forEach(key => (newParams[key] = params[key]))
  return newParams
}

export const channelSorter = (a: QrChannel, b: QrChannel): number => {
  const aChanCode = a.chanCode
  const bChanCode = b.chanCode
  if (aChanCode < bChanCode) {
    return -1
  }

  if (aChanCode > bChanCode) {
    return 1
  }

  // if chanCode is equal, sort by chanMerID
  const aID = a.chanMerID
  const bID = b.chanMerID
  if (aID < bID) {
    return -1
  }

  if (aID > bID) {
    return 1
  }

  return 0
}

// 计算金额的正负号
// 退款类的交易需要在交易金额前补负号
export const signAmtNumberOfTrade = (trade: ITrade): number => {
  const refundTransScenes = [
    'api-refund',
    'doco-refund',
    'mis-refund',
    'pt-refund',
    'term-refund',
    'web-chan-refund',
    'web-refund',
    'web-ATH'
  ]

  if (refundTransScenes.indexOf(trade.transScene) === -1) {
    return trade.transAmt
  }

  return -1 * trade.transAmt
}

export const ecChannelSorter = (a: ECUPIChannel, b: ECUPIChannel): number => {
  const aChanCode = a.chanCode
  const bChanCode = b.chanCode
  if (aChanCode < bChanCode) {
    return -1
  }

  if (aChanCode > bChanCode) {
    return 1
  }

  // if chanCode is equal, sort by chanMerID
  const aID = a.acquiringIIN
  const bID = b.acquiringIIN
  if (aID < bID) {
    return -1
  }

  if (aID > bID) {
    return 1
  }

  return 0
}
// 一维对象数组去重
export const objectArrayDeduplication = (arr: any[], type: string): any[] => {
  const empty = new Map()
  return arr.filter((item: any) => !empty.has(item[type]) && empty.set(item[type], 1))
}

export const getUniqueKey = (chanCode: CardChanCodeValue): string => {
  switch (chanCode) {
    case CardChanCodeValue.AME:
      return 'ain'
    case CardChanCodeValue.UPI:
      return 'ain'
    case CardChanCodeValue.MCC:
    case CardChanCodeValue.VIS:
      return 'bin'
  }
}

// 按照渠道的value值排序(字母序)
export const channelOptionSort = (a: IChannelOption, b: IChannelOption): number => {
  const aValue = a.value.toUpperCase()
  const bValue = b.value.toUpperCase()
  if (aValue < bValue) {
    return -1
  }

  if (aValue > bValue) {
    return 1
  }
  return 0
}

/** 新平台只支持某几个渠道，但老平台的数据可能有多个，所以要做筛选 */
export const filterChannel = (chanCodes: any[]): any[] => {
  return chanCodes.filter(c => SUPPORT_CHANNELS.has(c))
}

/**
 * 获取标准时区的时间
 * @param i 为时区值，例如中国北京为东八区则输入8
 */
export const getLocalTime = (i: number): Date => {
  if (typeof i !== 'number') return
  const d = new Date()
  const len = d.getTime()
  const offset = d.getTimezoneOffset() * 60000
  const utc = len + offset
  return new Date(utc + 3600000 * i)
}
